﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Linq;

namespace MvvmTreeView
{
    public static class TreeNodeCollectionExtensions
    {
        public static TreeNodeOrderedCollection InsertOrderBy<TKey>(this TreeNodeCollection treeNodeCollection, Expression<Func<ITreeNode, TKey>> keySelector)
        {
            var collection = new TreeNodeOrderedCollection(treeNodeCollection);

            collection.Expressions.Add(keySelector, CollectionOperation.InsertAscending);

            return collection;
        }

        public static void InsertOrderBy<TKey>(this TreeNodeCollection treeNodeCollection, Expression<Func<ITreeNode, TKey>> keySelector, ITreeNode treeNode)
        {
            var collection = new TreeNodeOrderedCollection(treeNodeCollection);

            collection.Expressions.Add(keySelector, CollectionOperation.InsertAscending);

            collection.PerformInsert(treeNode);
        }

        public static TreeNodeOrderedCollection ThenBy<TKey>(this TreeNodeOrderedCollection treeNodeCollection, Expression<Func<ITreeNode, TKey>> keySelector)
        {
            treeNodeCollection.Expressions.Add(keySelector, CollectionOperation.InsertAscending);

            return treeNodeCollection;
        }

        public static void ThenBy<TKey>(this TreeNodeOrderedCollection treeNodeCollection, Expression<Func<ITreeNode, TKey>> keySelector, ITreeNode treeNode)
        {
            treeNodeCollection.Expressions.Add(keySelector, CollectionOperation.InsertAscending);

            treeNodeCollection.PerformInsert(treeNode);
        }
    }

    public class TreeNodeOrderedCollection
    {
        private Dictionary<System.Linq.Expressions.LambdaExpression, CollectionOperation> expressions;
        private TreeNodeCollection rootCollection;

        public Dictionary<System.Linq.Expressions.LambdaExpression, CollectionOperation> Expressions
        {
            get 
            {
                if (expressions == null)
                {
                    expressions = new Dictionary<System.Linq.Expressions.LambdaExpression, CollectionOperation>();
                }

                return expressions; 
            }
        }

        public void PerformInsert(ITreeNode treeNode)
        {
            IEnumerable<ITreeNode> lastSet = null;
            IEnumerable<ITreeNode> currentSet = rootCollection.Where(n => !(n is SpinsterNode));
            string lastSortMember = null;

            foreach (var keyValuePair in expressions)
            {
                var expression = keyValuePair.Key;
                var operation = keyValuePair.Value;
                var sortMember = ((System.Linq.Expressions.MemberExpression)expression.Body).Member;

                // todo - build sort expressions dynamically instead of assuming existing sort property possibilities

                switch (sortMember.Name)
                {
                    case "Text":

                        lastSet = currentSet;
                        currentSet = currentSet.Where(t => t.Text == treeNode.Text);

                        break;

                    case "ChildOrdinal":

                        lastSet = currentSet;
                        currentSet = currentSet.Where(t => t.ChildOrdinal == treeNode.ChildOrdinal);

                        break;

                    default:
                        break;
                }

                lastSortMember = sortMember.Name;

                if (currentSet.Count() == 0)
                {
                    break;
                }
            }

            if (currentSet.Count() > 0)
            {
                var last = currentSet.Last();
                var insertIndex = rootCollection.IndexOf(last) + 1;

                rootCollection.Insert(insertIndex, treeNode);
            }
            else
            {
                switch (lastSortMember)
                {
                    case "Text":

                        var last = lastSet.TakeWhile(t => string.Compare(t.Text, treeNode.Text) < 0).LastOrDefault();

                        if (last != null)
                        {
                            var insertIndex = rootCollection.IndexOf(last) + 1;
                            rootCollection.Insert(insertIndex, treeNode);
                        }
                        else
                        {
                            var first = lastSet.FirstOrDefault();

                            if (first != null)
                            {
                                var insertIndex = rootCollection.IndexOf(first);
                                rootCollection.Insert(insertIndex, treeNode);
                            }
                            else
                            {
                                rootCollection.Insert(0, treeNode);
                            }
                        }

                        break;

                    case "ChildOrdinal":

                        last = lastSet.TakeWhile(t => t.ChildOrdinal < treeNode.ChildOrdinal).LastOrDefault();

                        if (last != null)
                        {
                            var insertIndex = rootCollection.IndexOf(last) + 1;
                            rootCollection.Insert(insertIndex, treeNode);
                        }
                        else
                        {
                            var first = lastSet.FirstOrDefault();

                            if (first != null)
                            {
                                var insertIndex = rootCollection.IndexOf(first);
                                rootCollection.Insert(insertIndex, treeNode);
                            }
                            else
                            {
                                rootCollection.Insert(0, treeNode);
                            }
                        }

                        break;
                }
            }
        }

        public TreeNodeOrderedCollection(TreeNodeCollection treeNodeCollection)
        {
            rootCollection = treeNodeCollection;
        }
    }

    public enum CollectionOperation
    {
        InsertAscending,
        InsertDescending,
        Sort
    }

    public class TreeNodeCollection : ObservableCollection<ITreeNode>
    {
    }
}
